package com.terren.spider.core.html.downloader;

import java.io.IOException;

import org.apache.http.annotation.ThreadSafe;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;

import com.terren.spider.entity.config.SysConfig;

import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.downloader.HttpClientRequestContext;
import us.codecraft.webmagic.proxy.Proxy;

@ThreadSafe
public class AbuyunHttpClientDownloader extends BasicHttpClientDownloader {

	private AbuyunHttpUriRequestConverter httpUriRequestConverter = new AbuyunHttpUriRequestConverter();

	private PoolingHttpClientConnectionManager connectionManager = null;
	private static CredentialsProvider credsProvider = null;

	public AbuyunHttpClientDownloader() {
		initHttpClientConnectionManager();
		initCredentialsProvider();
	}

	public void setHttpUriRequestConverter(AbuyunHttpUriRequestConverter httpUriRequestConverter) {
		this.httpUriRequestConverter = httpUriRequestConverter;
	}

	private CloseableHttpClient getHttpClient(Site site) {
		// if (site == null) {
		// return httpClientGenerator.getClient(null);
		// }
		String domain = site.getDomain();
		CloseableHttpClient httpClient = httpClients.get(domain);
		if (httpClient == null) {
			synchronized (this) {
				httpClient = httpClients.get(domain);
				if (httpClient == null) {
					// httpClient = httpClientGenerator.getClient(site);
					httpClient = HttpClients.custom().setConnectionManager(connectionManager)
							.setDefaultCredentialsProvider(credsProvider).build();
					httpClients.put(domain, httpClient);
				}
			}
		}
		return httpClient;
	}

	/**
	 * 定制部分 初始化HttpClientConnectionManager
	 */
	private void initHttpClientConnectionManager() {
		if (null == connectionManager) {
			ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
			LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
			Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
					.register("http", plainsf).register("https", sslsf).build();

			connectionManager = new PoolingHttpClientConnectionManager(registry);
			// connectionManager.setMaxTotal(20);
			connectionManager.setDefaultMaxPerRoute(5);
		}

	}

	/**
	 * 定制部分 初始化CredentialsProvider
	 */
	private void initCredentialsProvider() {
		if (null == credsProvider) {
			credsProvider = new BasicCredentialsProvider();
			credsProvider.setCredentials(AuthScope.ANY,
					// new AuthScope(proxyHost, proxyPort),
					new UsernamePasswordCredentials(SysConfig.ABUYUN_PROXY_USER, SysConfig.ABUYUN_PROXY_PASS));
		}
	}

	@Override
	public Page download(Request request, Task task) {
		if (task == null || task.getSite() == null) {
			throw new NullPointerException("task or site can not be null");
		}
		CloseableHttpResponse httpResponse = null;
		CloseableHttpClient httpClient = getHttpClient(task.getSite());

		Proxy proxy = new Proxy(SysConfig.ABUYUN_PROXY_HOST, SysConfig.ABUYUN_PROXY_PORT);
		HttpClientRequestContext requestContext = httpUriRequestConverter.convert(request, task.getSite(), proxy);

		Page page = Page.fail();
		int statusCode = 0;
		try {
			httpResponse = httpClient.execute(requestContext.getHttpUriRequest(),
					requestContext.getHttpClientContext());
			statusCode = httpResponse.getStatusLine().getStatusCode();
			logger.info("statusCode: " + statusCode);

			page = handleResponse(requestContext, request,
					request.getCharset() != null ? request.getCharset() : task.getSite().getCharset(), httpResponse,
					task);
			onSuccess(request);
			logger.info("[statusCode: " + statusCode + "] downloading page success {}", request.getUrl());
			return page;
		} catch (IOException e) {
			logger.warn("[statusCode: " + statusCode + "] download page {} error", request.getUrl(), e);
			onError(request);
			return page;
		} finally {
			if (httpResponse != null) {
				// ensure the connection is released back to pool
				EntityUtils.consumeQuietly(httpResponse.getEntity());
			}
			if (proxyProvider != null && proxy != null) {
				proxyProvider.returnProxy(proxy, page, task);
			}
		}
	}

	@Override
	public void setThread(int thread) {
		// httpClientGenerator.setPoolSize(thread);
		Integer total = 20;
		if (thread < 20 && thread > 0) {
			total = thread;
		}
		connectionManager.setMaxTotal(total);
	}

}
